package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.UserUtil;
import org.oslims.client.service.AnalysisResultServiceAsync;
import org.oslims.client.service.ParameterResultServiceAsync;
import org.oslims.client.service.RequestServiceAsync;
import org.oslims.client.service.SampleServiceAsync;
import org.oslims.client.uicomponents.LabeledCheckBox;
import org.oslims.client.uicomponents.MeasurementTimeCell;
import org.oslims.client.uicomponents.ParameterResultCell;
import org.oslims.client.uicomponents.TagTreeItem;
import org.oslims.client.uicomponents.WidgetUtils;
import org.oslims.client.view.impl.ValidateResultsViewImpl;
import org.oslims.domain.AbstractSample;
import org.oslims.domain.Analysis;
import org.oslims.domain.AnalysisResult;
import org.oslims.domain.LabStaff;
import org.oslims.domain.ParameterResult;
import org.oslims.domain.Request;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.TreeItem;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = ValidateResultsViewImpl.class)
public class ValidateResultsPresenter extends BasePresenter<ValidateResultsViewImpl, MainEventBus> {

	private AnalysisResultServiceAsync analysisResultService;
	private SampleServiceAsync sampleService;
	private ParameterResultServiceAsync parameterResultService;
	private RequestServiceAsync requestService;

	Map<String, Analysis> analysisMap = new HashMap<String, Analysis>();
	Map<String, AbstractSample> sampleMap = new HashMap<String, AbstractSample>();

	List<LabeledCheckBox> analysisSelectionList = new ArrayList<LabeledCheckBox>();
	List<LabeledCheckBox> sampleSelectionList = new ArrayList<LabeledCheckBox>();

	Map<String, Void> expandedNodes = new HashMap<String, Void>();

	private String validateTo;
	private String currentState;

	public ValidateResultsPresenter() {

	}

	public void onShowLabValidateResults(DockLayoutPanel mainWidget) {
		WidgetUtils.removeCenterWidget(mainWidget);
		mainWidget.add(view.getViewWidget());

		view.getHeaderLabel().setText("Lab Validate Results");
		validateTo = "Lab Validated";
		currentState = "Measured";

		expandedNodes.clear();
		refreshView();
	}

	public void onShowScientValidateResults(DockLayoutPanel mainWidget) {
		WidgetUtils.removeCenterWidget(mainWidget);
		mainWidget.add(view.getViewWidget());

		view.getHeaderLabel().setText("Scientifical validation of results");
		validateTo = "Scient. Validated";
		currentState = "Lab Validated";

		refreshView();
	}

	public void bind() {

		view.getRequestTree().addOpenHandler(new OpenHandler<TreeItem>() {

			@Override
			public void onOpen(OpenEvent<TreeItem> arg0) {
				final TagTreeItem ti = (TagTreeItem) arg0.getTarget();

				if (!expandedNodes.containsKey(ti.getText()))
					expandedNodes.put(ti.getText(), null);

				loadDataForItem(ti);
			}

		});

		view.getRequestTree().addCloseHandler(new CloseHandler<TreeItem>() {

			@Override
			public void onClose(CloseEvent<TreeItem> arg0) {
				final TagTreeItem ti = (TagTreeItem) arg0.getTarget();

				if (expandedNodes.containsKey(ti.getText()))
					expandedNodes.remove(ti.getText());
			}
		});

		view.getValidateSelectedAnalysisButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {

				List<AnalysisResult> selectedResults = new ArrayList<AnalysisResult>();

				for (LabeledCheckBox box : analysisSelectionList) {
					AnalysisResult ar = (AnalysisResult) box.getTag();
					ar.setState(validateTo);
					selectedResults.add(ar);
				}

				analysisResultService.validateAnalysisResults(selectedResults, new AsyncCall<Void>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(Void result) {
						for (LabeledCheckBox box : analysisSelectionList) {
							box.getCheckBox().setValue(false);
						}

						for (LabeledCheckBox box : sampleSelectionList) {
							box.getCheckBox().setValue(false);
						}
						analysisSelectionList.clear();
						sampleSelectionList.clear();

						refreshView();
					}
				});
			}
		});

		view.getValidateSelectedSamplesButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				List<AbstractSample> selectedSamples = new ArrayList<AbstractSample>();

				for (LabeledCheckBox box : sampleSelectionList) {
					AbstractSample as = (AbstractSample) box.getTag();
					as.setState(validateTo);
					selectedSamples.add(as);
				}

				sampleService.validateSampleResults(selectedSamples, new AsyncCall<Void>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(Void result) {

						for (LabeledCheckBox box : analysisSelectionList) {
							box.getCheckBox().setValue(false);
						}

						for (LabeledCheckBox box : sampleSelectionList) {
							box.getCheckBox().setValue(false);
						}
						analysisSelectionList.clear();
						sampleSelectionList.clear();

						refreshView();
					}
				});
			}
		});
	}

	private void loadDataForItem(final TagTreeItem ti) {
		if (ti.getTag() instanceof AbstractSample) {
			getAnalysisResultsForSample(ti);
		} else if (ti.getTag() instanceof Request) {
			getSamplesForRequest(ti);
		}
	}

	private void getSamplesForRequest(final TagTreeItem ti) {

		sampleService.getAllSamplesByRequest((Request) ti.getTag(), new AsyncCall<List<AbstractSample>>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(List<AbstractSample> result) {
				ti.removeItems();

				for (AbstractSample as : result) {
					TagTreeItem tiSample = new TagTreeItem();
					tiSample.setTag(as);

					final LabeledCheckBox sampleSelectionBox = new LabeledCheckBox();
					sampleSelectionBox.setTag(as);
					sampleSelectionBox.setText(as.getCode());

					sampleSelectionBox.getCheckBox().addValueChangeHandler(new ValueChangeHandler<Boolean>() {

						@Override
						public void onValueChange(ValueChangeEvent<Boolean> event) {
							if (event.getValue()) {
								sampleSelectionList.add(sampleSelectionBox);
							} else {
								sampleSelectionList.remove(sampleSelectionBox);
							}
						}
					});

					tiSample.setWidget(sampleSelectionBox);

					ti.addItem(tiSample);
					tiSample.addItem("...");

					if (expandedNodes.containsKey(tiSample.getText())) {
						GWT.log("Expanding node: " + tiSample.getText());
						tiSample.setState(true, true);
					}
				}
			}
		});
	}

	private void getAnalysisResultsForSample(final TagTreeItem ti) {

		analysisResultService.getAnalysisResultsBySampleAndState((AbstractSample) ti.getTag(), currentState, new AsyncCall<List<AnalysisResult>>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(List<AnalysisResult> result) {
				ti.removeItems();

				AbstractSample sample = (AbstractSample) ti.getTag();
				List<AbstractSample> sampleList = new ArrayList<AbstractSample>();
				sampleList.add(sample);

				final CellTable<AbstractSample> sampleInformationTable = new CellTable<AbstractSample>(1);

				sampleInformationTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getOrigin();
					}
				}, "Origin");

				sampleInformationTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getDescription();
					}
				}, "Description");

				sampleInformationTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getSampleType().getName();
					}
				}, "Sample Type");

				sampleInformationTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getState();
					}
				}, "State");

				sampleInformationTable.setData(0, 1, sampleList);

				ti.addItem(sampleInformationTable);

				for (AnalysisResult ar : result) {

					TreeItem tiAnalysisName = new TreeItem();

					final LabeledCheckBox analysisSelection = new LabeledCheckBox();

					analysisSelection.setText(ar.getAnalysis().getName());
					analysisSelection.setLabelStyleName("analysisNameTreeItem");
					analysisSelection.setTag(ar);

					analysisSelection.getCheckBox().addValueChangeHandler(new ValueChangeHandler<Boolean>() {

						@Override
						public void onValueChange(ValueChangeEvent<Boolean> event) {
							if (event.getValue()) {
								analysisSelectionList.add(analysisSelection);
							} else {
								analysisSelectionList.remove(analysisSelection);
							}
						}
					});

					tiAnalysisName.setWidget(analysisSelection);

					ti.addItem(tiAnalysisName);

					final CellTable<ParameterResult> table = new CellTable<ParameterResult>(ar.getParameterResults().size());

					table.addColumn(new TextColumn<ParameterResult>() {

						@Override
						public String getValue(ParameterResult object) {
							return object.getParameter().getName();
						}
					}, "Parameter");

					Column<ParameterResult, ParameterResult> measurementTime = new Column<ParameterResult, ParameterResult>(new MeasurementTimeCell()) {

						@Override
						public ParameterResult getValue(ParameterResult object) {
							return object;
						}
					};

					table.addColumn(measurementTime, "Meas Time");

					measurementTime.setFieldUpdater(new FieldUpdater<ParameterResult, ParameterResult>() {

						@Override
						public void update(int index, ParameterResult object, ParameterResult value) {
							object.setMeasuredAt(value.getMeasuredAt());
						}
					});

					Column<ParameterResult, ParameterResult> resultCol = new Column<ParameterResult, ParameterResult>(new ParameterResultCell(false)) {

						@Override
						public ParameterResult getValue(ParameterResult object) {
							if (object.getMeasuredAt() == null)
								object.setMeasuredAt(new Date());
							return object;
						}
					};

					table.addColumn(resultCol, "Result");

					resultCol.setFieldUpdater(new FieldUpdater<ParameterResult, ParameterResult>() {

						@Override
						public void update(int index, ParameterResult object, ParameterResult value) {
							object = value;
						}
					});

					Column<ParameterResult, String> saveResultCol = new Column<ParameterResult, String>(new ButtonCell()) {

						@Override
						public String getValue(ParameterResult object) {

							if (object.getState().equals(validateTo)) {
								return "Re-Analyze";
							} else {
								return "Validate";
							}
						}
					};

					table.addColumn(saveResultCol);

					saveResultCol.setFieldUpdater(new FieldUpdater<ParameterResult, String>() {

						@Override
						public void update(int index, ParameterResult object, String value) {

							if (object.getState().equals(validateTo)) {
								object.setState("In Analyzing");
							} else {
								object.setState(validateTo);
							}
							saveParameterResult(object);
							table.redraw();
						}
					});

					table.setPageSize(ar.getParameterResults().size());
					table.setData(0, ar.getParameterResults().size(), ar.getParameterResults());

					ti.addItem(table);
				}
			}
		});
	}

	private void saveParameterResult(ParameterResult object) {

		if (validateTo.equals("Lab Validated")) {
			object.setLabValBy(UserUtil.getTestOperator());
		} else {
			object.setScientValBy((LabStaff) UserUtil.getLoggedInUser());
		}

		parameterResultService.saveOrUpdateParameterResult(object, new AsyncCall<Void>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(Void result) {
				refreshView();
			}
		});
	}

	public void refreshView() {

		view.getRequestTree().clear();

		analysisSelectionList.clear();
		sampleSelectionList.clear();

		requestService.getRequestByState("Released", new AsyncCall<List<Request>>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(List<Request> result) {
				view.getRequestTree().clear();

				for (Request r : result) {
					TagTreeItem ti = new TagTreeItem();
					ti.setTag(r);
					ti.setText(r.getCode());
					view.getRequestTree().addItem(ti);
					ti.addItem("...");

					if (expandedNodes.containsKey(ti.getText())) {
						ti.setState(true, true);
					}
				}
			}
		});
	}

	@InjectService
	public void setAnalysisResultService(AnalysisResultServiceAsync analysisResultService) {
		this.analysisResultService = analysisResultService;
	}

	@InjectService
	public void setSampleService(SampleServiceAsync sampleService) {
		this.sampleService = sampleService;
	}

	@InjectService
	public void setParameterResultService(ParameterResultServiceAsync parameterResultService) {
		this.parameterResultService = parameterResultService;
	}

	@InjectService
	public void setRequestService(RequestServiceAsync requestService) {
		this.requestService = requestService;
	}

}
